package com.gooluke.gateway.config;

import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.AbstractListener;
import com.alibaba.nacos.api.exception.NacosException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Map;

/**
 * 这个配置的作用就两点：
 * 1、 获取路由的动态配置
 * 2、 监听动态路由的变化并及时刷新
 */
@Configuration
public class NacosDynamicRouteRepositoryConfig implements ApplicationEventPublisherAware, ApplicationRunner, RouteDefinitionRepository {
    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(NacosDynamicRouteRepositoryConfig.class);
    ApplicationEventPublisher publisher;
    @Autowired
    NacosConfigManager nacosConfigManager;
    @Value(value = "${dynamic-routes.data-id:gateway-routes}")
    private String dataId;
    @Value(value = "${dynamic-routes.group:DEFAULT_GROUP}")
    private String group;

    @Override
    public void run(ApplicationArguments args) throws NacosException {
        ConfigService configService = nacosConfigManager.getConfigService();
        configService.addListener(dataId, group, new NacosRouteRefreshListener(this));
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        try {
            // 从Nacos获取路由配置
            String config = nacosConfigManager.getConfigService().getConfig(dataId, group, 5000L);
            if (StringUtils.isEmpty(config)) {
                return Flux.empty();
            }
            // 解析JSON配置
            //List<RouteDefinition> routes = objectMapper.readValue(config, new TypeReference<>() {});
            //解析YAML配置，更加直观，并且方便添加
            List<RouteDefinition> routes = parseYamlRoutes(config);
            return Flux.fromIterable(routes);
        } catch (Exception e) {
            throw new RuntimeException("从Nacos加载路由配置失败", e);
        }
    }

    @Override
    public Mono<Void> save(Mono<RouteDefinition> route) {
        return Mono.error(new UnsupportedOperationException("暂不支持动态添加路由"));
    }

    @Override
    public Mono<Void> delete(Mono<String> routeId) {
        return Mono.error(new UnsupportedOperationException("暂不支持动态删除路由"));
    }

    /**
     * 监听器
     */
    private class NacosRouteRefreshListener extends AbstractListener {
        RouteDefinitionRepository routeDefinitionRepository;

        public NacosRouteRefreshListener(RouteDefinitionRepository routeDefinitionRepository) {
            this.routeDefinitionRepository = routeDefinitionRepository;
        }

        @Override
        public void receiveConfigInfo(String config) {
            publisher.publishEvent(new RefreshRoutesEvent(routeDefinitionRepository));
        }
    }


    /**
     * 解析YAML配置
     */
    private List<RouteDefinition> parseYamlRoutes(String config) {
        try {
            // 1. 打印原始配置检查
            log.debug("Raw YAML config:\n{}", config);

            // 2. 使用Spring的YamlPropertySourceLoader
            YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
            PropertySource<?> propertySource = loader.load("gateway-routes", new ByteArrayResource(config.getBytes())).getFirst();

            // 3. 创建Environment并绑定
            StandardEnvironment environment = new StandardEnvironment();
            environment.getPropertySources().addFirst(propertySource);

            // 4. 绑定配置
            GatewayRouteProperties properties = new GatewayRouteProperties();
            Binder binder = Binder.get(environment);
            binder.bind("spring.cloud.gateway", Bindable.ofInstance(properties));

            // 5. 结果检查
            if (CollectionUtils.isEmpty(properties.getRoutes())) {
                log.warn("路由配置为空，请检查YAML格式");
                Map<String, Object> sourceMap = (Map<String, Object>) propertySource.getSource();
                log.debug("完整的绑定属性树：\n{}", sourceMap);
            }

            return properties.getRoutes() != null ? properties.getRoutes() : List.of();
        } catch (Exception e) {
            log.error("YAML解析失败，内容：\n{}\n错误：", config, e);
            return List.of();
        }
    }

}
